//
// xmlparse.cpp
// xmlint
// An XML-based interface for the Protea Project / xmlparse.cpp handles the XML parsing functions
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "../SDK/ToolsInterface.h"
#include "../OSDep/utf8.h"
#include "xmlparse.h"
#include "../defns.h"
using namespace ProteaApp1;

#include <stdio.h>
#include <stdarg.h>
#include <time.h>

xmlint_parser::xmlint_parser() 
{
	info = NULL;
	node_data = NULL;
	myapp = NULL;
	state_info = NULL;
	Reset();
};

xmlint_parser::~xmlint_parser() 
{
	Cleanup();	
};

void xmlint_parser::Cleanup() 
{
	// Cleanup node_data
	if (node_data) 
	{
		delete [] node_data;
		node_data = NULL;
	};

	// Clean state records
	StateInfo* temp = state_info;
	while (temp) 
	{
		StateInfo* deleteme = temp;
		temp = temp->next;
		delete deleteme;
	};
	state_info = NULL;
};


void xmlint_parser::Reset() 
{
	Cleanup();

	// Cleanup state records
	transaction_id = 0;
	info = NULL;
	PushState( STATE_START );

	// Allocate data...
	node_data_size = 1024;
	node_data = new char[ node_data_size ];

	buffer_count = 0;
};

void xmlint_parser::ShowOutput() 
{
	switch( state ) 
	{
		case STATE_START: printf( "START\n" ); break;
		case STATE_EXCALIBUR: printf ("EXCALIBUR\n"); break;
		case STATE_TRANSACTIONLIST: printf( "TRANS LIST\n" ); break;
		case STATE_TRANSACTION: printf( "TRANS\n" ); break;
		case STATE_BUFFERLIST: printf( "BUF LIST\n" ); break;
		case STATE_BUFFER: printf( "BUFFER\n" ); break;
		case STATE_FIELD: printf( "FIELD\n" ); break;
		case STATE_MESSAGELIST: printf( "MES LIST\n" ); break;
		case STATE_MESSAGE: printf( "MESSAGE\n" ); break;

		case STATE_QUERY: printf( "QUERY\n" ); break;
		case STATE_QUERYBUFFER: printf( "QUERY BUF\n" ); break;
		case STATE_FIELDLIST: printf( "FIELD LIST\n" ); break;
		case STATE_QUERYFIELD: printf( "QUERY FIELD\n" ); break;
		case STATE_ORDERLIST: printf( "ORDERLIST\n" ); break;
		case STATE_ORDER: printf( "ORDER\n" ); break;
		case STATE_WHERELIST: printf( "WHERELIST\n" ); break;
		case STATE_WHERE: printf( "WHERE\n" ); break;
		case STATE_UNKNOWN: printf( "UNKNOWN\n" ); break;
	};
};

void xmlint_parser::PushState( XML_STATE new_state ) 
{
	state = new_state;

	StateInfo* temp = new StateInfo;
	temp->next = state_info;
	temp->state = state;
	state_info = temp;

//	printf( "--> " );
//	ShowOutput();
};

XML_STATE xmlint_parser::PopState() 
{
	XML_STATE old_state = STATE_UNKNOWN;

	StateInfo* temp = state_info;
	state_info = state_info->next;
	old_state = temp->state;
	delete temp;

//	printf( "<-- " );
//	ShowOutput();

	if (state_info)
	{
		state = state_info->state;
	}
	else
	{
		state = STATE_UNKNOWN;
	}
//	printf( "CURRENT:" );
//	ShowOutput();
	return old_state;
};

void xmlint_parser::HandleState( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	switch( state ) 
	{
		case STATE_TRANSACTIONLIST:
			HandleTransactionList( atts, start );
			break;
		case STATE_TRANSACTION:
			HandleTransaction( atts, start );
			break;
		case STATE_BUFFER:
			HandleBuffer( atts, start );
			break;
		case STATE_FIELD:
			HandleField( atts, start );
			break;
		case STATE_QUERY:
			HandleQuery( atts, start );
			break;
		case STATE_QUERYBUFFER:
			HandleQueryBuffer( atts, start );
			break;
		case STATE_QUERYFIELD:
			HandleQueryField( atts, start );
			break;
		case STATE_ORDER:
			HandleQueryOrder( atts, start );
			break;
		case STATE_WHERE:
			HandleQueryWhere( atts, start );
			break;
		default:
			break;
	};
};

// Error handlers
void xmlint_parser::warning( const XERCES_CPP_NAMESPACE::SAXParseException& exception ) 
{
	UniTranslator message( exception.getMessage() );
	throw XMLParseException( message.UTF8() );
};

void xmlint_parser::error( const XERCES_CPP_NAMESPACE::SAXParseException& exception ) 
{
	UniTranslator message( exception.getMessage() );
	throw XMLParseException( message.UTF8() );
};

void xmlint_parser::fatalError( const XERCES_CPP_NAMESPACE::SAXParseException& exception ) 
{
	UniTranslator message( exception.getMessage() );
	throw XMLParseException( message.UTF8() );
};


// SAX Parser
void xmlint_parser::startElement( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const XERCES_CPP_NAMESPACE::Attributes& atts ) 
{
	UniTranslator name( localname );

	// Reset the data pointer
	*node_data = 0;
	node_data_ptr = node_data;

	XML_STATE new_state = STATE_UNKNOWN;

	// Check state tree
	switch( state ) 
	{
		case STATE_START:
			if (strcmp( name, "eXcalibur" )==0)
			{
				new_state = STATE_EXCALIBUR;
			}
			break;

		case STATE_EXCALIBUR:
			if (strcmp( name, "TransactionList" )==0)
			{
				new_state = STATE_TRANSACTIONLIST;
			}
			else if (strcmp( name, "Query" )==0)
			{
				new_state = STATE_QUERY;
			}
			break;

		case STATE_TRANSACTIONLIST:
			if (strcmp( name, "Transaction" )==0)
			{
				new_state = STATE_TRANSACTION;
			}
			break;

		case STATE_TRANSACTION:
			if (strcmp( name, "BufferList" )==0)
			{
				new_state = STATE_BUFFERLIST;
			}
			else if (strcmp( name, "MessageList" )==0)
			{
				new_state = STATE_MESSAGELIST;
			}
			break;

		case STATE_BUFFERLIST:
			if (strcmp( name, "Buffer" )==0)
			{
				new_state = STATE_BUFFER;
			}
			break;

		case STATE_BUFFER:
			if (strcmp( name, "Field" )==0)
			{
				new_state = STATE_FIELD;
			}
			break;

		case STATE_MESSAGELIST:
			if (strcmp( name, "Message" )==0)
			{
				new_state = STATE_MESSAGE;
			}
			break;

		case STATE_QUERY:
			if (strcmp( name, "Buffer" )==0)
			{
				new_state = STATE_QUERYBUFFER;
			}
			break;

		case STATE_QUERYBUFFER:
			if (strcmp( name, "FieldList" )==0)
			{
				new_state = STATE_FIELDLIST;
			}
			else if (strcmp( name, "OrderList" )==0)
			{
				new_state = STATE_ORDERLIST;
			}
			else if (strcmp( name, "WhereList" )==0)
			{
				new_state = STATE_WHERELIST;
			}
			break;

		case STATE_FIELDLIST:
			if (strcmp( name, "Field" )==0)
			{
				new_state = STATE_QUERYFIELD;
			}
			break;

		case STATE_ORDERLIST:
			if (strcmp( name, "Order" )==0)
			{
				new_state = STATE_ORDER;
			}
			break;

		case STATE_WHERELIST:
			if (strcmp( name, "Where" )==0)
			{
				new_state = STATE_WHERE;
			}
			break;

		default:
			new_state = STATE_UNKNOWN;
			break;
	};

	PushState( new_state );
	HandleState( &atts, true );
};

void xmlint_parser::characters( const XMLCh* const chars, const unsigned int length ) 
{
	UniTranslator xml_data( chars );

	// Do we need to allocate more room?
	unsigned int used = node_data_ptr - node_data;
	if (used + length > node_data_size) 
	{
		// Reallocate...
		int new_size = used + length + 1024;
		char* new_data = new char[ new_size ];
		memcpy( new_data, node_data, used );
		delete [] node_data;

		node_data = new_data;
		node_data_ptr = node_data + used;
		node_data_size = new_size;
	};

	node_data_ptr += sprintf( node_data_ptr, "%s", xml_data.UTF8() );
};



void xmlint_parser::endElement( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname ) 
{
	HandleState( NULL, false );

	*node_data = 0;
	XML_STATE last_state = PopState();
};



void xmlint_parser::HandleBuffer( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	if (start) 
	{
		// Get properties
		long number = 0;
		char* type = NULL;
		char* name = NULL;
		bool result = true;

		try 
		{
			// Formulate the message
			XCBuffer* buf = NULL;
			XCToolsMessage* data_send = NULL;

			int len = atts->getLength();
			for (int i=0; i<len; i++) 
			{
				UniTranslator pname( atts->getLocalName( i ) );
				UniTranslator pvalue( atts->getValue( i ) );

//				cout << "Prop: " << pname << " = " << pvalue << endl;
				if (strcmp( pname, "Number" )==0) 
				{
					number = atoi( pvalue );
				} 
				else if (strcmp( pname, "Name" )==0) 
				{
					name = strdup( pvalue );
				} 
				else if (strcmp( pname, "Type" )==0) 
				{
					type = strdup( pvalue );
				}
				else
				{
					throw XMLParseInvalidAttr( pname, "Buffer" );
				}
			};

			if (!number) 
			{
				if (!name)
				{
					throw XMLParseMissingAttr( "Name and/or Number", "Buffer" );
				}
				else 
				{
					// Look up number based on name
					buffer_format* formatinfo = myapp->GetFormatInfo( name );
					if (formatinfo)
					{
						number = formatinfo->number;
					}
					else 
					{
						printf( "Bad format: '%s'\n", name );
						throw XMLParseMissingAttr( "Valid Name", "Buffer" );
					};
				};
			};

			if (!type)
			{
				throw XMLParseMissingAttr( "Type", "Buffer" );
			}

			assert( myapp );
//			cout << "APPLICATION: " << myapp->app_number << "  " << myapp->app->GetAppNumber() << endl;

			if (type) 
			{
				if (! strcmp( type, "Header" ) )
				{
					datatype = TRANS_HEADER_TYPE;
				}
				else if ( ! strcmp( type, "Detail" ) )
				{
					datatype = TRANS_DETAIL_TYPE;
				}
				else
				{
					throw XMLParseInvalidAttr( "Type", "Buffer" );
				}
			} 
			else
			{
				throw XMLParseMissingAttr( "Type", "Buffer" );
			}

			// Allocate the actual buffer
			current_buffer = myapp->CreateBuffer( number, false );
//			printf( "CREATE BUFFER: %d\n", number );
		} 
		catch( ... ) 
		{
			if (type)
			{
				free( type );
			}
			if (name)
			{
				free( name );
			}
			throw;
		};

		// Cleanup
		if (type)
		{
			free(type);
		}
		if (name)
		{
			free(name);
		}
	} 
	else 
	{
		// Add the buffer to the transaction
		XCToolsMessage data_send( myapp );
		data_send.CreateMsg( TRANS_MESSAGE_SIZE + current_buffer->GetMsgSize() );
		data_send.SetHeader( msg_trans, src_int, dest_app, false, true );

		trans_message* trans_info = (trans_message*)data_send.GetDataPtr();
		trans_info->format = current_buffer->GetFormatNumber();
		trans_info->data_type = datatype;
		trans_info->appnbr = info->GetApplNumber();
		trans_info->order = buffer_count++;

		current_buffer->PutIntoMessage( (unsigned char*)trans_info->data, current_buffer->GetMsgSize() );
		info->AddMessage( &data_send, trans_info->data_type );
		current_buffer->Release();
		current_buffer = NULL;
	};
};

void xmlint_parser::HandleField( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	if (start) 
	{
		char* name = NULL;
		char* number = NULL;

		try 
		{
			bool retval = true;
			long phase = 0;

			// Get field information
			int len = atts->getLength();
			for (int i=0; i<len; i++) 
			{
				UniTranslator pname( atts->getLocalName( i ) );
				UniTranslator pvalue( atts->getValue( i ) );

		//		cout << "Prop: " << pname << " = " << pvalue << endl;
				if (strcmp( pname, nametag ) == 0)
				{
					name = strdup( pvalue );
				}
				else if (strcmp( pname, numbertag ) == 0)
				{
					number = strdup( pvalue );
				}
				else
				{
					throw XMLParseInvalidAttr( pname, "Field" );
				}
			};

			// Obtain a pointer to our field
			int field_num = 0;
			if (number)
			{
				field_num = atoi( number );
			}
			else if (name) 
			{
				buffer_field* fld = myapp->GetFieldInfo( name );
				if (fld)
				{
					field_num = fld->number;
				}
			};

			if (!field_num)
			{
				throw XMLParseInvalidAttr( "Number", "Field" );
			}

			current_field = current_buffer->GetField( field_num );
			if (!current_field) 
			{
				printf( "FIELD NAME:%s NUMBER:%s DOESN'T EXIST ON BUFFER %s\n", name ? name : "* UNKNOWN *", number ? number : "* UNKNOWN *", current_buffer->GetFormatName() );
			};
		} 
		catch( ... ) 
		{
			if (name)
			{
				free( name );
			}
			if (number)
			{
				free( number );
			}
			throw;
		};
		// Cleanup
		if (name)
		{
			free( name );
		}
		if (number)
		{
			free( number );
		}
	} 
	else 
	{
		if (current_field) 
		{
			// We should have a field value...
			int type = current_field->GetType();
			if (type == 6) 
			{
				current_field->SetShort( atoi( node_data ) );
			}
			else if (type == 7) 
			{
				current_field->SetLong( atoi( node_data ) );
			}
			else if (type == XCBLOB) 
			{
				current_field->SetBLOBbase64( strlen(node_data), node_data );
			}
			else 
			{
				current_field->SetString( node_data );
			}
//			printf( "Field %s = '%s'\n", fld->GetFieldName(), value.UTF8() );

			// If the field was truncated, let the user know
			if (current_field->GetTruncationFlag()) 
			{
				throw XCToolsError( ERR_FIELD_TRUNCATION, __LINE__, myapp, current_field->GetFieldNumber(), 0, 1, current_field );
			};
		};
	};
};



void xmlint_parser::HandleTransaction( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start )
{
	static int total_transactions = 0;

//	cout << "[HandleTransaction]: AppNumber = " << curdefaults->app_nbr << endl;

	if (start) 
	{
		char* user = curdefaults.user ? strdup( curdefaults.user ) : NULL;
		char* password = curdefaults.pass ? strdup( curdefaults.pass ) : NULL;
		char* type = curdefaults.type ? strdup( curdefaults.type ) : NULL;
		char* name = NULL;
		char* description = NULL;
		char* action = NULL;
		char* supertrans = NULL;
		char* phase = NULL;

		try 
		{
			long lang = curdefaults.lang;
			int session = curdefaults.session;
			app_nbr = curdefaults.app_nbr;
			transaction_id = 0;

			long test_id = 0;
			bool Ignore = false;
			long number = 0;

			// Handle properties
			int len = atts->getLength();
			for (int i=0; i<len; i++) 
			{
				UniTranslator pname( atts->getLocalName( i ) );
				UniTranslator pvalue( atts->getValue( i ) );

				if (strcmp( pname, applnumbertag )==0) 
				{
					// <Transaction ApplNumber="">
					app_nbr = atoi(pvalue);
				} 
				else if (strcmp( pname, typetag )==0) 
				{
					// <Transaction Type="">
					if (type)
					{
						free( type );
					}
					type = strdup( pvalue );
				} 
				else if (strcmp( pname, usernametag )==0) 
				{
					// <Transaction UserName="">
					if (user)
					{
						free( user );
					}
					user = strdup( pvalue );
				} 
				else if (strcmp( pname, passwordtag )==0) 
				{
					// <Transaction Password="">
					if (password)
					{
						free( password );
					}
					password = strdup( pvalue );
				} 
				else if (strcmp( pname, nametag )==0) 
				{
					// <Transaction Name="">
					name = strdup( pvalue );
				} 
				else if (strcmp( pname, idtag )==0) 
				{
					// <Transaction ID="">
					transaction_id = atoi( pvalue );
				} 
				else if (strcmp( pname, numbertag )==0) 
				{
					// <Transaction Number="">
					number = atoi( pvalue );
				} 
				else if (strcmp( pname, descriptiontag )==0) 
				{
					// <Transaction Description="">
					description = strdup( pvalue );
				} 
				else if (strcmp( pname, supertag )==0) 
				{
					// <Transaction Super="">
					supertrans = strdup( pvalue );
				} 
				else if (strcmp( pname, actiontag )==0) 
				{
					// <Transaction Action="">
					action = strdup( pvalue );
				} 
				else if (strcmp( pname, phasetag )==0) 
				{
					// <Transaction Phase="">
					phase = strdup( pvalue );
				} 
				else if (strcmp( pname, languagetag )==0) 
				{
					lang = atoi( pvalue );
				} 
				else if (strcmp( pname, sessiontag )==0) 
				{
					session = atoi( pvalue );
				} 
				else
				{
					throw XMLParseInvalidAttr( pname, "Transaction" );
				}
			};

		//	printf( "Leave properties!\n" );

			// Ensure that the transaction ID is submitted
			if (!transaction_id)
			{
				throw XMLParseMissingTag( "ID" );
			}

			// Attempt to connect to application information
			try 
			{
				xmlint.GetApplicationTransaction( myapp, app_nbr, info );
				if (!info)
				{
					assert( false );
				}
			} 
			catch( XCToolsException& error ) 
			{
				throw XMLParseException( error.GetDescription() );
			} 
			catch( ... ) 
			{
				throw XMLParseException( "Unknown error" );
			};

			// Handle transaction data
			if (!number) 
			{
				if (!name) 
				{
					throw XMLParseMissingAttr( "Name and/or Number", "Transaction" );
				} 
				else 
				{
					transaction_defn* trans = myapp->GetTransaction( name );
					if (trans)
					{
						number = trans->trans_number;
					}
					else 
					{
						printf( "Bad transaction name: %s\n", name );
						throw XMLParseMissingAttr( "Valid Name", "Transaction" );
					};
				};
			};

			if ( phase )
			{
				info->SetPhase( atoi( phase ) );
			}

			// Need a session value, if none available, use the socket handle
			if (!session)
			{
				session = (int)cursocket->GetInHandle();
			}

			// Set transaction properties
			info->SetTransID( transaction_id ); // Set transaction id
			info->SetTransNumber( number );
			info->SetUser( user );
			info->SetEncodedPass( password );
			info->SetSession( session );

			if (app_nbr == GCS_APP_NUMBER)
			{
				info->SetGCSTrans( true );
			}

			info->SetLang( lang );

			if ( supertrans )
			{
				info->SetSuperID( atol( supertrans ) );
			}
			info->SetAction( 0 );

		//cout << "ACTION : " << node->Action << endl;
			if ( action )
			{
				if ( !strcmp( action, "Create" ) )
				{
					info->SetAction( 1 );
				}
				if ( !strcmp( action, "Commit" ) )
				{
					info->SetAction( 2 );
				}
				if ( !strcmp( action, "Rollback" ) )
				{
					info->SetAction( 3 );
				}
			};
		} 
		catch( ... ) 
		{
			if (user) 
			{
				free( user );
			}
			if (password) 
			{
				free( password );
			}
			if (type) 
			{
				free( type );
			}
			if (name) 
			{
				free( name );
			}
			if (description) 
			{
				free( description );
			}
			if (action) 
			{
				free( action );
			}
			if (supertrans) 
			{
				free( supertrans );
			}
			if (phase) 
			{
				free( phase );
			}
			if (info) 
			{
				info->Release();
			}
			throw;
		};

		// Cleanup
			if (user) 
			{
				free( user );
			}
			if (password) 
			{
				free( password );
			}
			if (type) 
			{
				free( type );
			}
			if (name) 
			{
				free( name );
			}
			if (description) 
			{
				free( description );
			}
			if (action) 
			{
				free( action );
			}
			if (supertrans) 
			{
				free( supertrans );
			}
			if (phase) 
			{
				free( phase );
			}

	} 
	else 
	{
//		cout << "Send transaction!" << endl;
		xmlint.SendTransactionXML( curinfo, info );
//		cout << "Trans sent" << endl;

		// Cleanup the transaction info structure
		info->Release();
		info = NULL;

//		printf( "Leave transaction!\n" );
	};
};



void xmlint_parser::HandleTransactionList( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	// Reset the defaults
	memset( &curdefaults, 0, sizeof( XCDefaultType ) );

	if (start) 
	{
		int len = atts->getLength();
		for (int i=0; i<len; i++) 
		{
			UniTranslator propname( atts->getLocalName( i ) );
			UniTranslator propvalue( atts->getValue( i ) );

			if (strcmp( propname, applnumbertag )==0) 
			{
				// <TransactionList ApplNumber="">
				curdefaults.app_nbr = atoi( propvalue );
			} 
			else if (strcmp( propname, typetag )==0) 
			{
				// <TransactionList Type="">
				strcpy( curdefaults.type, propvalue );
			} 
			else if (strcmp( propname, usernametag )==0) 
			{
				// <TransactionList UserName="">
				strcpy( curdefaults.user, propvalue );
			} 
			else if (strcmp( propname, passwordtag )==0) 
			{
				// <TransactionList Password="">
				strcpy( curdefaults.pass, propvalue );
			} 
			else if (strcmp( propname, languagetag )==0) 
			{
				// <TransactionList Lang="">
				curdefaults.lang = atoi( propvalue );
			} 
			else if (strcmp( propname, sessiontag )==0) 
			{
				curdefaults.session = atoi( propvalue );
			} 
			else
			{
				throw XMLParseInvalidAttr( propname, "TransactionList" );
			}
		};
	};
};



void xmlint_parser::HandleQuery( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	if (start) 
	{
		long count = 0;
		long distinct = 0;
		long start_record = 0;
		const char* user = curdefaults.user;
		const char* password = curdefaults.pass;
		long lang = curdefaults.lang;
		int session = curdefaults.session;

		transaction_id = 0;
		app_nbr = curdefaults.app_nbr;

		// Handle properties
		int len = atts->getLength();
		for (int i=0; i<len; i++) 
		{
			UniTranslator pname( atts->getLocalName( i ) );
			UniTranslator pvalue( atts->getValue( i ) );

			if (strcmp( pname, applnumbertag )==0) 
			{
				// <TransactionList ApplNumber="">
				app_nbr = atoi( pvalue );
			} 
			else if (strcmp( pname, usernametag )==0) 
			{
				// <TransactionList UserName="">
				strcpy( curdefaults.user, pvalue );
			} 
			else if (strcmp( pname, passwordtag )==0) 
			{
				// <TransactionList Password="">
				strcpy( curdefaults.pass, pvalue );
			} 
			else if (strcmp( pname, languagetag )==0) 
			{
				// <TransactionList Language="">
				lang = atoi( pvalue );
			} 
			else if (strcmp( pname, idtag )==0) 
			{
				// <Transaction ID="">
				transaction_id = atoi( pvalue );
			} 
			else if (strcmp( pname, counttag )==0) 
			{
				// <Query Count="">
				count = atoi( pvalue );
			} 
			else if (strcmp( pname, starttag )==0) 
			{
				// <Query Start="">
				start_record = atoi( pvalue );
			} 
			else if (strcmp( pname, distincttag )==0) 
			{
				// <Query Distinct="">
				distinct = atoi( pvalue );
			} 
			else if (strcmp( pname, sessiontag )==0) 
			{
				session = atoi( pvalue );
			} 
			else
			{
				throw XMLParseInvalidAttr( pname, "Query" );
			}
		};

		// Check required parameters
		if (!transaction_id)
		{
			throw XMLParseMissingAttr( "ID", "Query" );
		}
		if (!app_nbr)
		{
			throw XMLParseMissingAttr( "ApplNumber", "Query" );
		}

		// Set the operating language
		XCSetLanguage( lang );

		try 
		{
			// This initializes the application, and may fail
			xmlint.GetApplicationTransaction( myapp, app_nbr, info );
		} 
		catch( XCToolsException& error ) 
		{
			throw XMLParseException( error.GetDescription() );
		} 
		catch( ... ) 
		{
			throw XMLParseException( "Unknown error" );
		};

		// Need a session value, if none available, use the socket handle
		if (!session)
		{
			session = (int)cursocket->GetInHandle();
		}

		// Set Transaction properties
		info->SetTransID( transaction_id );
		info->SetUser( user );
		info->SetEncodedPass( password );
		info->SetAction( distinct );
		info->SetQuery();
		info->SetRecordCount( count );
		info->SetStartRecord( start_record );
		info->SetSession( session );

		message = (sql_query_result_message*)new char[ 10240 ];
		mdata = message->data;

		message->appnbr = app_nbr;
		message->data_type = QUERY_FIELD_INFO;
		message->fieldcount = 0;
		message->wherecount = 0;
	} 
	else 
	{
		if (message->fieldcount) 
		{
			long size = mdata - message->data;
			XCToolsMessage* reply = new XCToolsMessage( info->GetOwner() );
			reply->CreateMsg( SQL_QUERY_RESULT_SIZE + size );
			reply->SetHeader( msg_sql_query );

			sql_query_result_message* temp = (sql_query_result_message*)reply->GetDataPtr();
			memcpy( temp, message, size + sizeof( sql_query_result_message ) );

			info->AddMessage( reply, message->data_type );
		};

		cout << "Send transaction!" << endl;
		xmlint.SendTransactionXML( curinfo, info );
//		cout << "Trans sent" << endl;

		info->Release();
//		printf( "Leave transaction!\n" );
	};
};



void xmlint_parser::HandleQueryBuffer( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	if (start) 
	{
		char* name = NULL;
		long number = 0;

		try 
		{
			// Handle properties
			int len = atts->getLength();
			for (int i=0; i<len; i++) 
			{
				UniTranslator pname( atts->getLocalName( i ) );
				UniTranslator pvalue( atts->getValue( i ) );

				if (strcmp( pname, nametag )==0) 
				{
					// <Buffer Name="">
					name = strdup( pvalue );
				} 
				else if (strcmp( pname, numbertag )==0) 
				{
					// <Buffer Number="">
					number = atoi( pvalue );
				} 
				else
				{
					throw XMLParseInvalidAttr( pname, "Query" );
				}
			};

			if (!number) 
			{
				if (!name)
				{
					throw XMLParseMissingAttr( "Name and/or Number", "Query" );
				}
				else 
				{
					buffer_format* format = myapp->GetFormatInfo( name );
					if (format)
					{
						number = format->number;
					}
					else 
					{
						printf( "Bad buffer name: %s\n", name );
						throw XMLParseMissingAttr( "Valid Name", "Query" );
					};
				};
			};

			// Set the buffer number
			info->SetTransNumber( number );
			message->transid = info->GetTransID();
		} 
		catch( ... ) 
		{
			if (name) 
			{
				free( name );
			}
			throw;
		};

		// Cleanup
		if (name) 
		{
			free( name );
		}
	};
};


void xmlint_parser::HandleQueryField( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	if (start) 
	{
		// Get field properties
		char* fname = NULL;
		char* fvalue = NULL;
		long fnumber = 0;
		long comp = 0;
		long order = 0;

		try 
		{
			int len = atts->getLength();
			for (int i=0; i<len; i++) 
			{
				UniTranslator pname( atts->getLocalName( i ) );
				UniTranslator pvalue( atts->getValue( i ) );

				if (strcmp( pname, nametag )==0) 
				{
					// <Field Name="">
					fname = strdup( pvalue );
				} 
				else if (strcmp( pname, numbertag )==0) 
				{
					// <Field Number="">
					fnumber = atoi( pvalue );
				} 
				else
				{
					throw XMLParseInvalidAttr( pname, "Field" );
				}
			};

			// Convert field name to number
			if (!fnumber) 
			{
				if (fname) 
				{
					buffer_field* fld = myapp->GetFieldInfo( fname );
					if (fld)
					{
						fnumber = fld->number;
					}
					else 
					{
						char temp[ 255 ];
						sprintf( temp, "Invalid field: %s", fname );
						throw XMLParseMissingAttr( temp, "Field" );
					};
				};
			};
			if (!fnumber)
			{
				throw XMLParseMissingAttr( "Valid Name and/or Number", "Field" );
			}

			// Make the field record in the query buffer
			query_field = (sql_query_field*)mdata;
			query_field->number = fnumber;
			query_field->ordernumber = 0;
			query_field->valuesize = 0;

			message->fieldcount++;
			mdata += sizeof( sql_query_field );
		} 
		catch( ... ) 
		{
			if (fname) 
			{
				free( fname );
			}
			if (fvalue) 
			{
				free( fvalue );
			}
			throw;
		};

		// Cleanup
		if (fname) 
		{
			free( fname );
		}
		if (fvalue) 
		{
			free( fvalue );
		}
	};
};




void xmlint_parser::HandleQueryOrder( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	if (start) 
	{
		// Get field properties
		long fnumber = 0;
		char* fname = NULL;
		char* fvalue = NULL;

		try 
		{
			long comp = 0;
			long order = 0;

			int len = atts->getLength();
			for (int i=0; i<len; i++) 
			{
				UniTranslator pname( atts->getLocalName( i ) );
				UniTranslator pvalue( atts->getValue( i ) );

				if (strcmp( pname, nametag )==0) 
				{
					// <Field Name="">
					fname = strdup( pvalue );
				} 
				else if (strcmp( pname, numbertag )==0) 
				{
					// <Field Number="">
					fnumber = atoi( pvalue );
				} 
				else if (strcmp( pname, valuetag )==0) 
				{
					// <Field Order="">
					if (strcmp( pvalue, "ASC" )==0)
					{
						order = 1;
					}
					else if (strcmp( pvalue, "DESC" )==0)
					{
						order = -1;
					}
					else
					{
						throw XMLParseInvalidAttrValue( pname, "Order", pvalue );
					}
				} 
				else
				{
					throw XMLParseInvalidAttr( pname, "Order" );
				}
			};

			// Check required tags
			if (!order)
			{
				throw XMLParseInvalidAttr( "Value", "Order" );
			}

			// Convert field name to number
			if (!fnumber) 
			{
				if (fname) 
				{
					buffer_field* fld = myapp->GetFieldInfo( fname );
					if (fld)
					{
						fnumber = fld->number;
					}
					else 
					{
						char temp[ 255 ];
						sprintf( temp, "Invalid field: %s", fname );
						throw XMLParseMissingAttr( temp, "Field" );
					};
				};
			};
			if (!fnumber)
			{
				throw XMLParseMissingAttr( "Valid Name and/or Number", "Order" );
			}

			// Locate the required field...
			query_field = (sql_query_field*)message->data;
			int count = message->fieldcount;
			while (count && query_field->number != fnumber) 
			{
				char* new_pointer = (char*)query_field + sizeof( sql_query_field ) + query_field->valuesize;
				query_field = (sql_query_field*)new_pointer;
				count--;
			};

			if (!count)
			{
				throw XMLParseInvalidAttr( "Number", "Order" );
			}

			// Update the order number
			query_field->ordernumber = order;
		} 
		catch( ... ) {
			// Cleanup
			if (fname) 
			{
				free( fname );
			}
			if (fvalue) 
			{
				free( fvalue );
			}
			throw;
		};

		// Cleanup
		if (fname) 
		{
			free( fname );
		}
		if (fvalue) 
		{
			free( fvalue );
		}
	};
};



void xmlint_parser::HandleQueryWhere( const XERCES_CPP_NAMESPACE::Attributes* atts, bool start ) 
{
	if (start) 
	{
		// Get field properties
		long fnumber = 0;
		char* fname = NULL;
		char* fvalue = NULL;

		char* value1 = NULL;
		char* value2 = NULL;

		long comp = 0;
		long pre = 0;
		long post = 0;
		long logical = 0;

		try 
		{
			int len = atts->getLength();
			for (int i=0; i<len; i++) 
			{
				UniTranslator pname( atts->getLocalName( i ) );
				UniTranslator pvalue( atts->getValue( i ) );

				if (strcmp( pname, nametag )==0) 
				{
					// <Field Name="">
					fname = strdup( pvalue );
				} 
				else if (strcmp( pname, numbertag )==0) 
				{
					// <Field Number="">
					fnumber = atoi( pvalue );
				} 
				else if (strcmp( pname, pretag )==0) 
				{
					pre = atoi( pvalue );
				} 
				else if (strcmp( pname, posttag )==0) 
				{
					post = atoi( pvalue );
				} 
				else if (strcmp( pname, "Value1" )==0) 
				{
					value1 = strdup( pvalue );
				} 
				else if (strcmp( pname, "Value2" )==0) 
				{
					value2 = strdup( pvalue );
				} 
				else if (strcmp( pname, "Logical" )==0) 
				{
					if (strcmp( pvalue, "AND" )==0)
					{
						logical = logic_and;
					}
					else if (strcmp( pvalue, "OR" )==0)
					{
						logical = logic_or;
					}
					else
					{
						throw XMLParseInvalidAttrValue( pname, "Where", pvalue );
					}
				} 
				else if (strcmp( pname, "Comparison" )==0) 
				{
					if (strcmp( pvalue, "<" )==0)
					{
						comp = cmp_lessthan;
					}
					else if (strcmp( pvalue, ">" )==0)
					{
						comp = cmp_greaterthan;
					}
					else if (strcmp( pvalue, "=" )==0)
					{
						comp = cmp_equals;
					}
					else if (strcmp( pvalue, "!=" )==0 || strcmp( pvalue, "<>" )==0)
					{
						comp = cmp_notequals;
					}
					else if (strcmp( pvalue, "<=" )==0)
					{
						comp = cmp_lessthanequals;
					}
					else if (strcmp( pvalue, ">=" )==0)
					{
						comp = cmp_greaterthanequals;
					}
					else if (strcmp( pvalue, "BETWEEN" )==0)
					{
						comp = cmp_between;
					}
					else if (strcmp( pvalue, "LIKE" )==0)
					{
						comp = cmp_like;
					}
					else
					{
						throw XMLParseInvalidAttrValue( pname, "Where", pvalue );
					}
				} 
				else
				{
					throw XMLParseInvalidAttr( pname, "Where" );
				}
			};

			// Check required tags
			if (!value1)
			{
				throw XMLParseInvalidAttr( "Value1", "Where" );
			}

			// Convert field name to number
			if (!fnumber) 
			{
				if (fname) 
				{
					buffer_field* fld = myapp->GetFieldInfo( fname );
					if (fld)
					{
						fnumber = fld->number;
					}
					else 
					{
						char temp[ 255 ];
						sprintf( temp, "Invalid field: %s", fname );
						throw XMLParseMissingAttr( temp, "Where" );
					};
				};
			};
			if (!fnumber)
			{
				throw XMLParseMissingAttr( "Valid Name and/or Number", "Where" );
			}

			// Locate the required field...
			// Update the order number
			sql_query_where* query_where = (sql_query_where*)mdata;
		
			// Write value1
			int size = strlen( value1 );
			query_where->value1size = size;
			if (size)
			{
				memcpy( query_where->data, value1, size );
			}

			// Write value2
			int size2 = value2 ? strlen( value2 ) : 0;
			query_where->value2size = size2;
			if (size2)
			{
				memcpy( query_where->data + size, value2, size2 );
			}

			query_where->compnumber = comp;
			query_where->logical = logical;
			query_where->number = fnumber;
			query_where->pre = pre;
			query_where->post = post;

			message->wherecount++;
			mdata += sizeof( sql_query_where ) + size + size2;
		} 
		catch( ... ) 
		{
			if (fname) 
			{
				free( fname );
			}
			if (fvalue) 
			{
				free( fvalue );
			}
			if (value1) 
			{
				free( value1 );
			}
			if (value2) 
			{
				free( value2 );
			}
			throw;
		};

		// Cleanup
			if (fname) 
			{
				free( fname );
			}
			if (fvalue) 
			{
				free( fvalue );
			}
			if (value1) 
			{
				free( value1 );
			}
			if (value2) 
			{
				free( value2 );
			}
	};
};
